package com.hzb.LNReader;

import java.io.File;
import java.io.IOException;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

import com.hzb.LNReader.reader.ContentDetector;
import com.hzb.LNReader.reader.FileReader;
import com.hzb.LNReader.reader.GestureListener;
import com.hzb.LNReader.reader.ReaderView;
import com.hzb.LNReader.util.ReaderPref;

public class ReaderActivity extends Activity {
	public static final String CONFIG = "config";
	public static final String DST_FILE = "dstFile";
	public static final String LOG = "ReaderActivity";

	private static boolean isFirstLoad = true;

	private int statusBarHeight;
	private int screenHeight;

	private ReaderRecord currReadFile;
	private FileReader fileReader;

	private FrameLayout frame;

	private TextView seekText;
	private SeekBar seekBar;
	private String oriProcess;
	private int dotIndex;

	private ReaderView view;
	private ContentDetector filler;
	
	private void initContext() {
		ReaderHistory.loadFrom(getFileStreamPath(CONFIG));
		ReaderPref.init(this);
		isFirstLoad = false;
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (isFirstLoad)
			initContext();

		setContentView(R.layout.readerlayout);

		seekText = (TextView) findViewById(R.id.controlProcess);
		seekBar = (SeekBar) findViewById(R.id.SeekBar);

		frame = (FrameLayout) findViewById(R.id.FrameLayout);
		screenHeight = getWindowManager().getDefaultDisplay().getHeight();

		openFile(getIntent().getStringExtra(DST_FILE));
		if (view != null) {
			extractInit();
		}
	}

	private void extractInit() {
		TextView pt = (TextView) findViewById(R.id.PercentMark);
		int fontColor = ReaderPref.getFontColor();
		pt.setTextColor(fontColor);
		pt = (TextView) findViewById(R.id.ReadProcess);
		pt.setTextColor(fontColor);
		
		seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			@Override
			public void onProgressChanged(SeekBar seekBar, int progress,
					boolean fromUser) {
				if (fromUser) {
					oriProcess = Integer.toString(progress);
					seekText.setText(oriProcess);
				}
			}

			@Override
			public void onStartTrackingTouch(SeekBar seekBar) {
			}

			@Override
			public void onStopTrackingTouch(SeekBar seekBar) {
			}
		});

		view.post(new Runnable() {
			@Override
			public void run() {
				Rect displayRect = new Rect();
				getWindow().getDecorView().getWindowVisibleDisplayFrame(
						displayRect);
				statusBarHeight = displayRect.top;

				if (ReaderPref.isFullScreen()) {
					getWindow().setFlags(
							WindowManager.LayoutParams.FLAG_FULLSCREEN,
							WindowManager.LayoutParams.FLAG_FULLSCREEN);
				}

				View tmp = findViewById(R.id.num9);
				int h = tmp.getHeight();
				for (int i = 1;; i++) {
					seekText.setTextSize(i);
					seekText.measure(MeasureSpec.UNSPECIFIED,
							MeasureSpec.UNSPECIFIED);
					if (seekText.getMeasuredHeight() > h) {
						seekText.setTextSize(i - 1);
						((TextView) findViewById(R.id.controlProcessMark))
								.setTextSize(i - 1);
						break;
					}
				}
			}
		});

	}
	
	@Override
	protected void onResume() {
		super.onResume();
		if (filler != null) {
			filler.resume();
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
		if (filler != null) {
			filler.pause();
		}
	}

	@Override
	public void onBackPressed() {
		View tmpView = findViewById(R.id.processControllPanel);
		if (tmpView.getVisibility() == View.INVISIBLE) {
			super.onBackPressed();
			exit();
		} else {
			tmpView.setVisibility(View.INVISIBLE);
		}
	}

	private void openFile(String filePath) {
		File dstFile;
		if (filePath != null) {
			dstFile = new File(filePath);
			if (!dstFile.isFile()) {
				openFileSelector();
				return;
			}
			currReadFile = ReaderHistory.openReadHistory(dstFile);
		} else {
			for (;;) {
				if (ReaderHistory.head == null) {
					openFileSelector();
					return;
				}
				currReadFile = ReaderHistory.head;
				if (currReadFile.file.isFile()) {
					dstFile = currReadFile.file;
					break;
				}
				currReadFile.remove();
			}
		}

		view = new ReaderView(this, currReadFile);
		view.setOnTouchListener(new OnTouchListener() {
			private GestureDetector detector = new GestureDetector(
					new GestureListener(view));

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				return detector.onTouchEvent(event);
			}
		});
		frame.removeAllViews();
		frame.addView(view);
		fileReader = new FileReader();
		Log.i(LOG, "Loading file " + dstFile);
		loadFile();
	}

	private void loadFile() {
		try {
			if (currReadFile.file.length() != currReadFile.fileSize) {
				Log.i(LOG,
						"The file modify since last read, seeking to recover");
				float process = currReadFile.process;
				currReadFile.reset();
				fileReader.fastLoadFile(currReadFile);
				currReadFile.startLineSeq = fileReader
						.ensureFileBufferByOffset((int) (currReadFile.fileSize * process));
			} else {
				fileReader.fastLoadFile(currReadFile);
			}
			filler = new ContentDetector(currReadFile, fileReader, view);
			filler.start();
			fastLoad(currReadFile.startLineSeq);
		} catch (Exception e) {
			showError(e);
		}
	}

	private void fastLoad(int seqFrom) throws IOException {
		view.reset(seqFrom);
		final int[] indexRange = fileReader.ensureFileBuffer(seqFrom,
				ReaderPref.getStartReadLine());
		final int beginIndex = indexRange[0];
		final int endIndex = indexRange[1];

		final String[] lineBuffer = fileReader.contentLine;
		final int[] lineOffset = fileReader.lineOffset;
		final int limit = lineBuffer.length;
		int minHeight = this.screenHeight + currReadFile.scrollOffset;

		view.createAppendar(lineOffset[beginIndex]);
		final int lastIndex;
		for (int i = indexRange[0];; i++) {
			if (i == limit)
				i = 0;
			if (view.getPendingHeight() > minHeight || i == endIndex) {
				lastIndex = i;
				break;
			}

			view.addItem(lineBuffer[i]);
		}

		view.post(new Runnable() {
			@Override
			public void run() {
				view.flushAppenderAfter(lineOffset[lastIndex]);
				view.scrollBy(0, currReadFile.scrollOffset);
				filler.resume();
			}
		});
	}

	private void exit() {
		close();
		ReaderHistory.save();
		finish();
	}

	private void close() {
		if (filler == null)
			return;
		filler.stop();
		filler = null;
		currReadFile.lastReadTime = System.currentTimeMillis();
		try {
			int[] displayInfo = view.getPosition();
			currReadFile.startLineSeq = displayInfo[0];
			int[] bufferIndex = fileReader.ensureFileBuffer(
					currReadFile.startLineSeq, 1);
			int index = bufferIndex[0];
			currReadFile.scrollOffset = displayInfo[1];
			currReadFile.fileOffset = fileReader.lineOffset[index];
			if (60 > fileReader.contentLine[index].length()) {
				currReadFile.summary = fileReader.contentLine[index];
			} else {
				currReadFile.summary = fileReader.contentLine[index].substring(
						0, 60)
						+ "……";
			}
			currReadFile.process = displayInfo[2];
		} catch (Exception e) {
			currReadFile.reset();
			Log.e(LOG, "Error updating read position", e);
		}
		fileReader = null;
		currReadFile.offer();
		currReadFile = null;
		view.removeAllViews();
		view.destroyDrawingCache();
		view = null;
	}

	private void showError(Exception e) {
		close();
		Builder dialog = new AlertDialog.Builder(this);
		dialog.setTitle("错误");
		if (e != null) {
			Log.e(LOG, "", e);
			dialog.setMessage("读取文件出错,点击OK重新选择文件。\n" + e.toString());
		} else {
			dialog.setMessage("读取文件出错,点击OK重新选择文件。");
		}
		dialog.setPositiveButton("OK", new OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				openFileSelector();
			}
		});
		dialog.show();
	}

	private void openFileSelector() {
		Intent intent = new Intent(this, FileSelectActivity.class);
		startActivity(intent);
		close();
		finish();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = this.getMenuInflater();
		inflater.inflate(R.menu.readermenu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		int id = item.getItemId();
		switch (id) {
		case R.id.openFile:
			openFileSelector();
			return true;
		case R.id.showHistory: {
			Intent intent = new Intent(this, ShowHistoryActivity.class);
			startActivity(intent);
			close();
			finish();
			return true;
		}
		case R.id.showSeekPanel:
			findViewById(R.id.processControllPanel).setVisibility(View.VISIBLE);
			int[] displayInfo = view.getPosition();
			oriProcess = Integer.toString(displayInfo[2]);
			dotIndex = -1;
			updateControlProcess();
			break;
		// 修改编码
		case R.id.gbk:
			if (id == R.id.gbk)
				currReadFile.encoding = "GBK";
		case R.id.utf8:
			if (id == R.id.utf8)
				currReadFile.encoding = "UTF-8";
		case R.id.big5:
			if (id == R.id.big5)
				currReadFile.encoding = "BIG-5";
			openFile(null);
			return true;
		case R.id.setting: {
			Intent intent = new Intent(this, SettingActivity.class);
			startActivity(intent);
			close();
			finish();
			return true;
		}
		case R.id.exit:
			exit();
			return true;
		}
		return false;
	}

	private float updateControlProcess() {
		float pro = 0;
		if (oriProcess.length() != 0) {
			seekText.setText(oriProcess);
			pro = Float.parseFloat(oriProcess);
			seekBar.setProgress((int) pro);
		} else {
			seekText.setText("0");
			seekBar.setProgress(0);
		}
		return pro;
	}

	public void onNumKeyPress(View v) {
		boolean goSeek = false;
		switch (v.getId()) {
		case R.id.num0:
			if (oriProcess.length() > 0) {
				oriProcess += "0";
			}
			break;
		case R.id.num1:
			oriProcess += "1";
			break;
		case R.id.num2:
			oriProcess += "2";
			break;
		case R.id.num3:
			oriProcess += "3";
			break;
		case R.id.num4:
			oriProcess += "4";
			break;
		case R.id.num5:
			oriProcess += "5";
			break;
		case R.id.num6:
			oriProcess += "6";
			break;
		case R.id.num7:
			oriProcess += "7";
			break;
		case R.id.num8:
			oriProcess += "8";
			break;
		case R.id.num9:
			oriProcess += "9";
			break;
		case R.id.numDot:
			if (dotIndex != -1)
				return;
			dotIndex = oriProcess.length();
			if (oriProcess.length() > 0) {
				oriProcess += ".";
			} else {
				oriProcess = "0.";
			}

			break;
		case R.id.numX:
			findViewById(R.id.processControllPanel).setVisibility(
					View.INVISIBLE);
			return;
		case R.id.numCls:
			dotIndex = -1;
			oriProcess = "0";
			break;
		case R.id.numOk:
			goSeek = true;
			break;
		case R.id.numBk:
			int len = oriProcess.length();
			if (len > 1) {
				if (oriProcess.charAt(len - 1) == '.')
					dotIndex = -1;
				oriProcess = oriProcess.substring(0, len - 1);
			} else {
				oriProcess = "0";
			}
			break;
		}
		if (dotIndex == -1) {
			if (oriProcess.length() > 2)
				oriProcess = oriProcess.substring(1);
		} else {
			if (oriProcess.length() == dotIndex + 3)
				oriProcess = oriProcess.substring(0, dotIndex + 1)
						+ oriProcess.charAt(dotIndex + 2);
		}
		float pro = updateControlProcess();
		if (goSeek) {
			try {
				seekTo(pro);
			} catch (Exception e) {
				showError(e);
			}
		}
	}

	private void seekTo(float percent) throws IOException {
		filler.pause();
		int seqFrom = fileReader
				.ensureFileBufferByOffset((int) (currReadFile.fileSize
						* percent / 100));
		fastLoad(seqFrom);
	}

	public int getStatusBarHeight() {
		return statusBarHeight;
	}
}
